package com.kk.apos;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kk.annotation.OperationLog;
import com.kk.pojo.entity.SysOperationLog;
import com.kk.service.SysOperationLogService;
import com.kk.utils.IpUtils;
import com.kk.utils.TraceIdUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 操作日志切面
 * 自动记录带有@OperationLog注解的方法的操作日志
 *
 * @author kk
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class OperationLogAspect {

    private final SysOperationLogService operationLogService;

    /** 使用 Spring 管理的 Jackson 序列化器（项目里一般都已有 Bean） */
    private final ObjectMapper objectMapper;

    /**
     * 定义切点：所有带有@OperationLog注解的方法
     */
    @Pointcut("@annotation(com.kk.annotation.OperationLog)")
    public void operationLogPointcut() {
    }

    /**
     * 环绕通知：记录操作日志
     */
    @Around("operationLogPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 获取请求信息
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取@OperationLog注解
        OperationLog operationLog = method.getAnnotation(OperationLog.class);

        // 构建操作日志对象
        SysOperationLog log = new SysOperationLog();
        log.setTraceId(TraceIdUtils.getTraceId());
        log.setOperationTime(LocalDateTime.now());

        // 设置用户信息
        setUserInfo(log);

        // 设置请求信息
        if (request != null) {
            log.setRequestMethod(request.getMethod());
            log.setRequestUrl(request.getRequestURI());
            log.setOperationIp(IpUtils.getIpAddr(request));
            log.setOperationLocation(IpUtils.getLocationByIp(log.getOperationIp()));
            log.setBrowser(IpUtils.getBrowser(request));
            log.setOs(IpUtils.getOs(request));
        }

        // 设置注解信息
        log.setModule(operationLog.module());
        log.setOperationType(operationLog.type().getCode());
        log.setDescription(operationLog.description());
        log.setMethod(method.getDeclaringClass().getName() + "." + method.getName());

        // 设置请求参数
        if (operationLog.saveRequestParams()) {
            setRequestParams(log, joinPoint, request);
        }

        // 执行目标方法
        Object result = null;
        try {
            result = joinPoint.proceed();

            // 设置操作状态为成功
            log.setOperationStatus(1);

            // 设置返回结果
            if (operationLog.saveResponseResult() && result != null) {
                String resultStr = toJsonSafe(result);
                log.setResponseResult(truncate(resultStr, 2000));
            }

        } catch (Throwable e) {
            // 设置操作状态为失败
            log.setOperationStatus(0);
            log.setErrorMsg(truncate(e.getMessage(), 2000));
            throw e;
        } finally {
            // 计算执行时长
            long endTime = System.currentTimeMillis();
            log.setExecuteTime((int) (endTime - startTime));

            // 异步保存日志
            operationLogService.recordOperationLog(log);
        }

        return result;
    }

    /**
     * 设置用户信息
     */
    private void setUserInfo(SysOperationLog log) {
        try {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) {
                log.setUsername(userDetails.getUsername());

                // 如果需要userId，可以从UserDetails中获取
                // if (userDetails instanceof CustomUserDetails cud) {
                //     log.setUserId(cud.getId());
                // }
            }
        } catch (Exception e) {
            OperationLogAspect.log.warn("获取用户信息失败", e);
        }
    }

    /**
     * 设置请求参数
     */
    private void setRequestParams(SysOperationLog log, ProceedingJoinPoint joinPoint,
                                  HttpServletRequest request) {
        try {
            // 获取方法参数
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                return;
            }

            // 过滤掉不需要记录的参数
            List<Object> logArgs = new ArrayList<>();
            for (Object arg : args) {
                if (shouldLogParam(arg)) {
                    logArgs.add(arg);
                }
            }

            // 参数为空则不记录
            if (logArgs.isEmpty()) {
                return;
            }

            // 如果是GET请求，记录query参数
            if (request != null && "GET".equalsIgnoreCase(request.getMethod())) {
                Map<String, String[]> parameterMap = request.getParameterMap();
                if (parameterMap != null && !parameterMap.isEmpty()) {
                    String queryJson = toJsonSafe(parameterMap);
                    log.setRequestParams(truncate(queryJson, 2000));
                }
            } else {
                // 其他请求方式记录方法参数
                String paramsJson = toJsonSafe(logArgs);
                log.setRequestParams(truncate(paramsJson, 2000));
            }

        } catch (Exception e) {
            OperationLogAspect.log.warn("获取请求参数失败", e);
        }
    }

    /**
     * 判断是否应该记录该参数
     */
    private boolean shouldLogParam(Object arg) {
        if (arg == null) return false;

        // 不记录以下类型的参数（流/请求/响应/上传文件/校验结果 等）
        if (arg instanceof HttpServletRequest
                || arg instanceof HttpServletResponse
                || arg instanceof MultipartFile
                || arg instanceof MultipartFile[]
                || arg instanceof InputStream
                || arg instanceof OutputStream
                || arg instanceof BindingResult) {
            return false;
        }
        return true;
    }

    /**
     * 使用 Jackson 安全序列化为 JSON（失败时降级为 toString）
     */
    private String toJsonSafe(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            OperationLogAspect.log.debug("JSON 序列化失败，降级为 toString()", e);
            return String.valueOf(obj);
        }
    }

    /**
     * 截断字符串
     */
    private String truncate(String str, int maxLength) {
        if (!StringUtils.hasText(str)) {
            return str;
        }
        if (str.length() <= maxLength) {
            return str;
        }
        return str.substring(0, maxLength) + "...";
    }
}
